package com.vaadin.thomas.contenttool.ui;

import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.vaadin.hene.expandingtextarea.ExpandingTextArea;
import org.vaadin.tokenfield.TokenField;

import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.event.LayoutEvents.LayoutClickEvent;
import com.vaadin.event.LayoutEvents.LayoutClickListener;
import com.vaadin.event.MouseEvents;
import com.vaadin.thomas.contenttool.data.Localization;
import com.vaadin.thomas.contenttool.data.Tag;
import com.vaadin.thomas.contenttool.data.Value;
import com.vaadin.thomas.contenttool.ui.events.LangChangedEvent;
import com.vaadin.thomas.contenttool.ui.events.LangChangedListener;
import com.vaadin.thomas.contenttool.ui.events.TagsChangedEvent;
import com.vaadin.thomas.contenttool.ui.events.ValueAddedEvent;
import com.vaadin.ui.AbstractOrderedLayout;
import com.vaadin.ui.AbstractSelect.Filtering;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.DateField;
import com.vaadin.ui.Embedded;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Layout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Window.CloseEvent;
import com.vaadin.ui.Window.CloseListener;

public class ExpandableValueComponent extends CssLayout implements
		LayoutClickListener, LangChangedListener {

	private static final String ICON_ARROW_DOWN = "icons/bullet_arrow_down";
	private static final String ICON_ARROW_RIGHT = "icons/bullet_arrow_right";

	private static final long serialVersionUID = -3478345973273982751L;

	private HorizontalLayout header;
	private CssLayout content;

	private Value val;

	private boolean initialized = false;
	private final boolean editable;

	private Embedded exp;

	public ExpandableValueComponent(Value value, boolean editable) {

		addStyleName("valuecomponent");
		setMargin(true, true, false, true);

		val = value;
		this.editable = editable;

		header = new HorizontalLayout();
		header.setMargin(false, true, false, false);
		header.addListener(this);
		header.setWidth(100, UNITS_PERCENTAGE);
		header.setSpacing(true);
		header.addStyleName("header");
		header.addStyleName("header-collapsed");
		header.addStyleName("cursor-hand");
		addComponent(header);

		content = new CssLayout();
		content.setVisible(false);
		content.setWidth(100, UNITS_PERCENTAGE);
		content.addStyleName("content");
		content.setMargin(false, true, false, true);
		// content.setSpacing(true);
		addComponent(content);

		refreshHeader();

		setWidth(100, UNITS_PERCENTAGE);
	}

	void refreshHeader() {
		header.removeAllComponents();

		exp = Util.getIcon("bullet_arrow_right");
		exp.setSource(Util.getResource(ICON_ARROW_RIGHT));
		exp.addStyleName("arrow");
		header.addComponent(exp);
		header.setComponentAlignment(exp, Alignment.MIDDLE_CENTER);

		Label identifier = new Label(val.getKey());
		identifier.addStyleName("cursor-hand");
		identifier.setSizeUndefined();
		header.addComponent(identifier);
		header.setExpandRatio(identifier, 1);

		Value valueFromCurrent = AdminMainLayout.ct().getCurrentVersion()
				.getValuesInThis().get(val.getKey());
		if (valueFromCurrent != null
				&& !valueFromCurrent.getLocalizations().isEmpty()) {
			Embedded e = Util.getIcon("asterisk_orange");
			e.setSizeUndefined();
			e.setDescription(AdminMainLayout.ct().get(
					"contenttool.content.item.ismodified"));
			header.addComponent(e);
			header.setComponentAlignment(e, Alignment.MIDDLE_CENTER);
		}

		for (Locale l : AdminMainLayout.ct().conf().getAllowedLanguages()) {
			Embedded e = Util.getFlag(l.getLanguage());
			e.setSizeUndefined();
			header.addComponent(e);
			header.setComponentAlignment(e, Alignment.MIDDLE_CENTER);

			if (!val.getAllLocalizations().containsKey(l)) {
				e.setEnabled(false);
			}
		}
	}

	void refreshContent() {

		content.removeAllComponents();

		if (val.isDeleted()) {

			HorizontalLayout hl = new HorizontalLayout();
			hl.setSpacing(true);
			hl.setMargin(false, true, false, true);

			Label itemDeletedLabel = new Label(AdminMainLayout.ct().get(
					"contenttool.content.item.isdeleted"));
			hl.addComponent(itemDeletedLabel);
			hl.setComponentAlignment(itemDeletedLabel, Alignment.MIDDLE_CENTER);
			Button unDeleteButton = new Button(AdminMainLayout.ct().get(
					"contenttool.content.item.undelete"));
			unDeleteButton.addListener(new UnDeleteListener());
			unDeleteButton.setEnabled(editable);
			hl.addComponent(unDeleteButton);

			content.addComponent(hl);

		} else {

			for (final Locale loc : AdminMainLayout.ct().conf()
					.getAllowedLanguages()) {

				List<Localization> locs = val.getAllLocalizations().get(loc);

				if (locs == null) {
					drawSingleLocalization(content, null, loc);
				} else {
					for (final Localization localization : locs) {
						drawSingleLocalization(content, localization, loc);
					}
				}
			}

			if (AdminMainLayout.ct().conf().allowTags()) {

				TokenField tagsField = new TagField();
				tagsField.setValue(val.getTags());
				tagsField.setEnabled(editable);
				content.addComponent(tagsField);
			}
		}
	}

	private void drawSingleLocalization(Layout layout,
			final Localization localization, final Locale loc) {

		HorizontalLayout hl = new HorizontalLayout();
		hl.setWidth(100, UNITS_PERCENTAGE);
		hl.setSpacing(true);

		hl.addStyleName("localization");

		Embedded e = Util.getFlag(loc.getLanguage());
		e.setSizeUndefined();
		hl.addComponent(e);
		hl.setComponentAlignment(e, Alignment.MIDDLE_CENTER);

		final ExpandingTextArea value = new ExpandingTextArea();
		value.setWidth(100, UNITS_PERCENTAGE);
		value.setValue(localization == null ? "" : localization.getValue());
		value.setEnabled(editable);
		value.addListener(new ValueChangeListener() {

			private static final long serialVersionUID = -986211935490113932L;

			@Override
			public void valueChange(ValueChangeEvent event) {

				AdminMainLayout
						.ct()
						.getModel()
						.setValue(
								val.getKey(),
								loc,
								value.getValue().toString(),
								localization == null ? null : localization
										.getStartDate());
				val = AdminMainLayout.ct().getCurrentVersion().getValue(val.getKey());
				refreshHeader();
				refreshContent();
			}
		});
		value.setImmediate(true);

		hl.addComponent(value);
		hl.setExpandRatio(value, 1);

		if (AdminMainLayout.ct().conf().allowTimedPublishing()) {
			// not allowed for base localization
			if (localization != null && localization.getStartDate() != null) {

				DateField start = new DateField();
				start.setWidth(130, UNITS_PIXELS);
				start.setLocale(AdminMainLayout.ct().getLang());
				start.setResolution(DateField.RESOLUTION_MIN);
				start.setValue(localization.getStartDate());
				start.addListener(new DateChangedListener(localization));
				start.setImmediate(true);
				start.setEnabled(editable);
				hl.addComponent(start);
				hl.setComponentAlignment(start, Alignment.MIDDLE_CENTER);

			}

			// not allowed for null localization
			if (localization != null && editable) {
				Embedded cal = Util.getIcon("clock_add");
				cal.setDescription(AdminMainLayout.ct().get(
						"contenttool.content.copy.copy_to_new"));
				cal.addStyleName("cursor-hand");
				hl.addComponent(cal);
				hl.setComponentAlignment(cal, Alignment.MIDDLE_CENTER);

				cal.addListener(new MouseEvents.ClickListener() {

					private static final long serialVersionUID = -9158135631343234642L;

					@Override
					public void click(
							com.vaadin.event.MouseEvents.ClickEvent event) {
						getWindow().addWindow(
								new AddLocalizationWithStartWindow(val,
										localization,
										ExpandableValueComponent.this));
					}
				});
				cal.setImmediate(true);
			}
		}

		e = Util.getIcon("asterisk_yellow");
		e.setSizeUndefined();

		boolean valueInPreview = AdminMainLayout.ct().getModel().getPreview()
				.getValuesInThis().containsKey(val.getKey());
		boolean langInPreview = valueInPreview
				&& AdminMainLayout.ct().getModel().getPreview().getValuesInThis()
						.get(val.getKey()).getLocalizations().containsKey(loc);
		e.setEnabled(langInPreview);
		if (langInPreview) {
			e.setDescription(AdminMainLayout.ct().get(
					"contenttool.content.item.ismodified"));
		}

		hl.addComponent(e);
		hl.setComponentAlignment(e, Alignment.MIDDLE_CENTER);

		Button previous = new Button();
		previous.setCaption(AdminMainLayout.ct().get(
				"contenttool.content.item.previous"));
		previous.addListener(new PreviousListener(loc));
		hl.addComponent(previous);
		hl.setComponentAlignment(previous, Alignment.MIDDLE_CENTER);

		Button clear = new Button();
		clear.addListener(new ClearListener(localization));
		clear.setCaption(AdminMainLayout.ct().get("contenttool.content.item.clear"));
		clear.setEnabled(editable);
		hl.addComponent(clear);
		hl.setComponentAlignment(clear, Alignment.MIDDLE_CENTER);

		layout.addComponent(hl);
	}

	@Override
	public void layoutClick(LayoutClickEvent event) {
		setExpanded(!content.isVisible());
	}

	@Override
	public void langChanged(LangChangedEvent event) {
		refreshContent();
	}

	Value getPossiblyNewValueForTags() {
		if (val.getVersion().isPreview()) {
			return val;
		}
		return val = AdminMainLayout.ct().getModel().addValue(val.getKey());
	}

	public void expand() {
		setExpanded(true);
	}

	public void collapse() {
		setExpanded(false);
	}

	private void setExpanded(boolean expanded) {
		if (!initialized) {
			refreshContent();
		}
		content.setVisible(expanded);
		if (expanded) {
			header.removeStyleName("header-collapsed");
			exp.setSource(Util.getResource(ICON_ARROW_DOWN));
		} else {
			header.addStyleName("header-collapsed");
			exp.setSource(Util.getResource(ICON_ARROW_RIGHT));
		}
	}

	private final class TagField extends TokenField {

		private static final long serialVersionUID = -4803590607614234688L;

		public TagField() {
			setLayout(new HorizontalLayout());
			((AbstractOrderedLayout) getLayout()).setSpacing(true);
			setTokenInsertPosition(InsertPosition.AFTER);
			setInputPrompt(AdminMainLayout.ct().get(
					"contenttool.content.item.tags_hint"));
			setNewTokensAllowed(true);
			setFilteringMode(Filtering.FILTERINGMODE_CONTAINS);
			setContainerDataSource(new TagContainer());
			setTokenCaptionPropertyId(TagContainer.CAPTION_PROPID);
			setRememberNewTokens(false);

			Label caption = new Label(AdminMainLayout.ct().get(
					"contenttool.content.item.tags_caption"));
			caption.setSizeUndefined();
			((HorizontalLayout) layout).addComponent(caption, 0);
			((HorizontalLayout) layout).setComponentAlignment(caption,
					Alignment.MIDDLE_LEFT);
		}

		@Override
		protected void onTokenInput(Object tokenId) {

			Tag t;
			if (tokenId instanceof Tag) {
				t = (Tag) tokenId;
			} else {
				t = AdminMainLayout.ct().getModel().getTag(tokenId.toString());
			}
			if (getContainerDataSource().getItem(t) == null) {
				getContainerDataSource().addItem(t);
				getContainerDataSource().getItem(t)
						.getItemProperty(TagContainer.CAPTION_PROPID)
						.setValue(t.getValue());
			}

			@SuppressWarnings("unchecked")
			Set<Object> set = (Set<Object>) getValue();
			if (set != null && set.contains(t)) {
				// duplicate
			} else {
				addToken(t);
			}
			getPossiblyNewValueForTags().addTag(t);
			AdminMainLayout.bb().fire(new TagsChangedEvent());
		}

		// show confirm dialog
		@Override
		protected void onTokenClick(final Object tokenId) {

			Tag t;
			if (tokenId instanceof Tag) {
				t = (Tag) tokenId;
			} else {
				t = AdminMainLayout.ct().getModel().getTag((String) tokenId);
			}
			removeToken(t);
			getPossiblyNewValueForTags().removeTag(t);
			AdminMainLayout.bb().fire(new TagsChangedEvent());
		}
	}

	private class ClearListener implements ClickListener {

		private static final long serialVersionUID = -2025814946676092853L;

		final Localization localization;

		public ClearListener(Localization localization) {
			this.localization = localization;
		}

		@Override
		public void buttonClick(ClickEvent event) {
			AdminMainLayout.ct().getModel().discardLocalization(localization);
			refreshHeader();
			refreshContent();
		}
	}

	private class UnDeleteListener implements ClickListener {

		private static final long serialVersionUID = -4533917662267828286L;

		@Override
		public void buttonClick(ClickEvent event) {
			val.unDelete();
			refreshContent();
			AdminMainLayout.bb().fire(new ValueAddedEvent(val));
		}
	}

	private class PreviousListener implements ClickListener {

		private static final long serialVersionUID = -4533917662267828286L;

		final Locale localization;

		public PreviousListener(Locale localization) {
			this.localization = localization;
		}

		@Override
		public void buttonClick(ClickEvent event) {
			Window w = new PreviousValuesWindow(val, localization, editable);
			getWindow().addWindow(w);

			w.addListener(new CloseListener() {

				private static final long serialVersionUID = 8465663960085535763L;

				@Override
				public void windowClose(CloseEvent e) {
					refreshHeader();
					refreshContent();
				}
			});
		}
	}

	private class DateChangedListener implements ValueChangeListener {

		private static final long serialVersionUID = 7950421796737822218L;

		private Localization l;

		public DateChangedListener(Localization l) {
			this.l = l;
		}

		@Override
		public void valueChange(ValueChangeEvent event) {
			Date newDate = (Date) event.getProperty().getValue();
			l.setStartDate(newDate);
		}

	}
}
